﻿using System;
using System.Collections;
using System.Collections.Generic;
using com.yoozoo.gta.Gameplay.RTS;
using UltimateGameTools;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
using XLua;
using Yoozoo.Gameplay.RTS.Proto;
using Random = UnityEngine.Random;

namespace Yoozoo.Gameplay.RTS
{
    public enum UnitType
    {
        Soldier = 1,
        Monster = 2,
        Commander = 3,
    }
    public enum MarchTeam
    {
        None = -1,
        Self = 0,
        Ally = 1,
        Enemy = 2,
        Other = 3
    }
    [GCOptimize]
    public enum EMarchStatus
    {
        Uninit = -1,
        Park = 0,
        WALK = 1,
        Battle = 2,
        Return = 3,
        Escape = 4, //die-5
        Collect = 5,
        SIEGE_FIGHT = 6,
        Chasing = 7,//追逐
        InBuilding = 8,
        Besiege = 9,//夹击
        CrossingGate = 10,
        ClientBattle = 11,//客户端提前开战表现
    }

    public enum DirectionEffectType
    {
        None = 0,
        Direction = 806,
        WithoutDirection = 807
    }
    /// <summary>
    /// 行军数据
    /// </summary>
    ///
    public enum EClientMarchType
    {
        None = -1,
        March,  //普通行军
        Monster,  //野怪
        Defence,   //驻防部队: 攻击玩家城的时候，城池内的部队
        Scout,   //侦察
        Aid,  //援助
        Building,  //建造部队
        Rescue,
        MapPoliceOffice,
        Common,
        Truck,
        Train,
    }
    public enum FormationType
    {
        Simple  = -1,
        Dead = 0,
        Normal = 1,
    }

    /// <summary>
    /// 部队音效的种类
    /// </summary>
    public enum EMarchAudioType
    {
        Move = 1,
        Battle = 2,
        Lose   = 3,
        Victory = 4,
    }

    public class EServerMarchType
    {
        public static string HOME_0 = "HOME_0";
        public static string ATTACK_1 = "ATTACK_1";
        public static string GARRISON_2 = "GARRISON_2";
        public static string COLLECT_3 = "COLLECT_3";
        public static string RETURN_4 = "RETURN_4";
        public static string DIE_5 = "DIE_5";
        public static string DEFENCE_6 = "DEFENCE_6";
        public static string SCOUT_7 = "SCOUT_7";
    }

    public enum EMarchMode
    {
        Guard,
        Normal,
    }
    public class BattleUtils
    {
        public static float ArmyScale = 0.5f;
        public static float HudScale = 0.7f;
        public static float EffectScale = 1f;
        public static bool IsSprite = true;
        private static int maxRage = 1000;
        public static float SelectedHudScale = 1.28f;
        public static float UnSelectedHudScale = 1f;
        public static Vector3 SelectPosition = new Vector3(0f, -43.7f, 0);
        public static Vector3 SpreadPosition = new Vector3(65f, -43.7f, 0);
        public static Vector3 DeadPosition = new Vector3(60f, -43.7f, 0);
        public static float TransparentAlpha = 0.6f;
        public static float OpaqueAlpha = 1f;
        public static int NormalAttackElement = 1000111;
        public static int NormalAttackSkill = 100011;

        public static float[] Ranges = {1.2f + 0.2f, 1.2f + 0.6f, 1.2f + 1.2f};
        public static Color whiteColor = new Color(0.831f, 0.815f, 0.819f);
        public static Color greenColor = new Color(0.341f, 0.827f, 0.192f);
        public static Color blueColor = new Color(0.294f, 0.741f, 0.925f);
        public static Color redColor = new Color(0.925f, 0.258f, 0.258f);

        public static int ScoutSoldierId = -200;
        public static string ScoutSoldierName = "gta_soilder_Scout_1";
        public static int ScureSoldierId = -201;
//        public static string ScureSoldierName = "gta_soilder_Scure_1";

        public static float CrossingGateTime = 8f;
        
        public static float GetRadius(int i)
        {
            if (i == 0) //部队
            {
                return 0.3f;
            }
            else if (i == 1) // 采矿
            {
                return 0.4f;
            }
            else if (i == 2)//城堡
            {
                return 0.6f;
            }
            return 0.3f;
        }

        public static Color GetColorByType(int type)
        {
            Color color = whiteColor;
            switch (type)
            {
                case 0:
                    color = MarchHelper.TeamColors[type];
                    break;
                case 1:
                    color = MarchHelper.TeamColors[type];
                    break;
                case 2:
                    color = MarchHelper.TeamColors[type];
                    break;
            }
            return color.linear;
        }

        public static float ChildUnitMinSpeed = 1 / 120f;
        public static float ChildUnitMaxSpeed = 1 / 60f;

        public const int ChildNum = 20;

        public static string GetSoldierModelNameById(int id)
        {
            if (id == ScoutSoldierId)
            {
                return ScoutSoldierName;
            }
            if (id == ScureSoldierId)
            {
                return ScoutSoldierName;
            }
            return ArmyManager.Instance.ResourceManager.GetModelNameById(id);
        }
        
        public static string GetLowQualitySoldierModelName(int soldierId,int soldierType)
        {
            if (soldierId == ScoutSoldierId)
            {
                return ScoutSoldierName;
            }
            if (soldierId == ScureSoldierId)
            {
                return ScoutSoldierName;
            }

            int id = 411011;
            switch (soldierType)
            {
                case 1:
                    id = 411011;
                    break;
                case 2:
                    id = 411021;
                    break;
                case 3:
                    id = 411031;
                    break;
                case 4:
                    id = 411041;
                    break;
            }

            return ArmyManager.Instance.ResourceManager.GetModelNameById(id);
        }
 
        public static int GetCurrentRangeType(float distance)
        {
            float tolerance = 0.2f;
            for (int i = 0; i < Ranges.Length; i++)
            {
                if (distance <= Ranges[i] + tolerance)
                {
                    return i;
                }
            }
            return Ranges.Length - 1;
        }

        public static float GetAnimatorSpeed(float totalTime, int totalFrames)
        {
            return totalFrames * 15f / totalTime;
        }

        private static int cachedFrame = 0;

        private static long cachedServerTimeMs = 0;

        public static long GetServerTimeMs()
        {
            if (cachedFrame == Time.frameCount)
            {
                return cachedServerTimeMs;
            }
            cachedFrame = Time.frameCount;
            cachedServerTimeMs = TimeUtils.GetServerTimeMs();
            return cachedServerTimeMs;
        }

        public static float GetValueStrFloat(Dictionary<string,float> dic, string key)
        {
            float value = 0;
            dic.TryGetValue(key, out value);
            return value;
        }

        public static void Shuffle<T>(ref List<T> list)
        {
            if (list.Count == 0)
            {
                return;
            }
            List<T> newList = new List<T>();
            foreach (T item in list)
            {
                newList.Insert(Random.Range(0, newList.Count), item);
            }
            newList.Remove(list[0]); //移除list[0]的�?
            newList.Insert(Random.Range(0, newList.Count), list[0]); //重新插入
            list = newList;
        }


        public static Vector3 GetCameraCenterPosition()
        {
            Transform transform = WorldCameraManager.mainCamera.transform;
            Vector3 position = transform.position;
            Vector3 forward = transform.forward;
            float angle = transform.localEulerAngles.x;
            Vector3 center = new Vector3(position.x, 0, position.z + position.y / Mathf.Tan(angle * Mathf.Deg2Rad));
            return center;
        }

        public static Vector3 GetServerForward(double angle)
        {
            return Quaternion.AngleAxis(-(float) angle, Vector3.up) * Vector3.right;
        }

        public static Vector3 GetPositionByForward(Vector3 originPosition,float rad)
        {
            return  new Vector3(originPosition.x * Mathf.Cos(rad) - originPosition.z * Mathf.Sin(rad),0,originPosition.x * Mathf.Sin(rad) + originPosition.z * Mathf.Cos(rad));
        }

        public static Vector3 GetPositionBySinAndCos(Vector3 originPosition,float sin, float cos)
        {
            return  new Vector3(originPosition.x * cos - originPosition.z * sin,0,originPosition.x * sin + originPosition.z * cos);
        }

        public static Dictionary<int, Dictionary<string, GPUSkinningAnim>> boneNodes =
            new Dictionary<int, Dictionary<string, GPUSkinningAnim>>();
        public static Dictionary<int, Dictionary<string, GPUSkinningBone>> gunNodes =
            new Dictionary<int, Dictionary<string, GPUSkinningBone>>();
        public static Dictionary<int, Dictionary<string, GPUSkinningBone>> throwNodes =
            new Dictionary<int, Dictionary<string, GPUSkinningBone>>();
        public static void AddBoneInfo(int index, GPUSkinning_BoneNode node)
        {
            var dic = new Dictionary<string,GPUSkinningAnim>();
            boneNodes.Add(index,dic);
            for (int i = 0; i < node.anims.Count; i++)
            {
                dic.Add(node.anims[i].animName,node.anims[i]);
            }

            var dic2 = new Dictionary<string, GPUSkinningBone>();
            gunNodes.Add(index,dic2);
            for (int i = 0; i < node.anims.Count; i++)
            {
                for (int j = 0; j < node.anims[i].bones.Count; j++)
                {
                    if (node.anims[i].bones[j].boneType == BoneBinder.BoneType.Gun)
                    {
                        dic2.Add(node.anims[i].animName,node.anims[i].bones[j]);
                        break;
                    }
                }
            }

            var dic3 = new Dictionary<string, GPUSkinningBone>();
            throwNodes.Add(index,dic3);
            for (int i = 0; i < node.anims.Count; i++)
            {
                for (int j = 0; j < node.anims[i].bones.Count; j++)
                {
                    if (node.anims[i].bones[j].boneType == BoneBinder.BoneType.Throw)
                    {
                        dic3.Add(node.anims[i].animName,node.anims[i].bones[j]);
                        break;
                    }
                }
            }
        }

        public static void RemoveBoneInfo(int index)
        {
            boneNodes.Remove(index);
            gunNodes.Remove(index);
            throwNodes.Remove(index);
        }

        public static int GetBonePointCount(int index)
        {
            if (boneNodes.TryGetValue(index, out var nodes))
            {
                foreach (var item in nodes)
                {
                    int count = 0;
                    for (int i = 0; i < item.Value.bones.Count; i++)
                    {
                        if (item.Value.bones[i].boneType == BoneBinder.BoneType.Hit)
                        {
                            count++;
                        }
                    }
                    return count;
                }
            }
            return 0;
        }

        public static Vector3 GetBonePoint(int index,string actionName,int frame,int boneIndex = 0)
        {
            if (boneNodes.TryGetValue(index, out var nodes))
            {
                if (nodes.TryGetValue(actionName, out var anim))
                {
                    if (anim.bones.Count > boneIndex)
                    {
                        if (anim.bones[boneIndex].frames.Count > frame)
                        {
                            return anim.bones[boneIndex].frames[frame].position;
                        }
                    }
                }
            }
            return Vector3.zero;
        }

        public static Vector3 GetGunPoint(int index, string actionName, int frame)
        {
            if (gunNodes.TryGetValue(index, out var nodes))
            {
                if (nodes.TryGetValue(actionName, out var anim))
                {
                    if (anim.frames.Count > frame)
                    {
                        return anim.frames[frame].position;
                    }
                    return anim.frames[0].position;
                }
            }
            return Vector3.zero;
        }

        public static Vector3 GetThrowPoint(int index, string actionName, int frame)
        {
            if (gunNodes.TryGetValue(index, out var nodes))
            {
                if (nodes.TryGetValue(actionName, out var anim))
                {
                    if (anim.frames.Count > frame)
                    {
                        return anim.frames[frame].position;
                    }
                    return anim.frames[0].position;
                }
            }
            return Vector3.zero;
        }

        //规则：最多为4位数,最多2位小数：千3433，万13.32K，十万143.4K 百万 1.54M
        public static string GetNumStr(int number)
        {
            int million = 1000000;
            int kilo = 1000;
            if (number >= million)
            {
                var intCount = GetIntCount(number, million);
                var decimalCount = 4 - intCount;
                decimalCount = Mathf.Clamp(decimalCount,0, 2);
                return $"{Math.Round((float)number / million, decimalCount).ToString()}M";
            }
            else if (number >= kilo)
            {
                var intCount = GetIntCount(number, kilo);
                if (intCount <= 1)
                {
                    return number.ToString();
                }
                var decimalCount = 4 - intCount;
                decimalCount = Mathf.Clamp(decimalCount,0, 2);
                return $"{Math.Round((float)number / kilo, decimalCount).ToString()}K";
            }
            return number.ToString();
        }

        //获取整数部分的位数
        private static int GetIntCount(int originNumber,int count)
        {
            int c = 0;
            var left = originNumber / count;
            while (left > 0)
            {
                c++;
                left /= 10;
            }
            return c;
        }
        
        
        public static void CopyArray<T>(NativeArray<T> source,NativeArray<T> target) where T : struct
        {
            for (int i = 0; i < source.Length; i++)
            {
                if (target.Length > i)
                {
                    target[i] = source[i];
                }
            }
        }

        public static NativeArray<T> GetExpendedArray<T>(NativeArray<T> originArray,int newSize) where T : struct
        {
            var newArray = new NativeArray<T>(newSize, Allocator.Persistent);
            CopyArray(originArray,newArray);
            originArray.Dispose();
            return newArray;
        }

        public static float NativeArrayInitRatio = 0.2f;

        public static string ServerDataToString(RtsServerInfo serverData)
        {
            return ObjectToString(serverData, "TroopMarch");
        }

        private static string tableFormat = "        ";
        
        public static string ObjectToString(object data, string name, int tableCount = 0,int depthLimited = 5,HashSet<string> ignoreClassName = null)
        {
            if (ignoreClassName != null && ignoreClassName.Contains(name))
            {
                return "";
            }
            if (data == null)
            {
                return string.Empty;
            }
            
            var type = data.GetType();
            var str = string.Empty;
            var tableEmptyStr = string.Empty;
            for (int i = 0; i < tableCount; i++)
            {
                tableEmptyStr += tableFormat;
            }
            if (depthLimited > 0 && tableCount > depthLimited)
            {
                return string.Format("{0}{1}={2}\n",tableEmptyStr, name, data);
            }
            //tableCount = tableCount + 1;
            if (type.IsValueType)
            {
                if (type.IsPrimitive || type.IsEnum)
                {
                    str += string.Format("{0}{1}={2}\n",tableEmptyStr, name, data);
                }
                else
                {
                    str += StructToString(data, name, tableCount,depthLimited,ignoreClassName);
                }
            }
            else if (type.IsArray)
            {
                str += string.Format("{0}{1}=\n",tableEmptyStr, name);
                var array = (System.Array) data;
                str += tableEmptyStr;
                str += "{\n";
                tableCount = tableCount + 1;
                for (int i = 0; i < array.Length; i++)
                {
                    str += ObjectToString(array.GetValue(i),"["+i+"]",tableCount,depthLimited,ignoreClassName);
                }
                str += tableEmptyStr;
                str += "}\n";
            }
            /*else if (type.IsArrayOrList())
            {
                
            }*/
            else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
            {
                str += ListToString(data, name, tableCount, depthLimited);
            }
            else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
            {
                //str += DictionaryToString(data, name, tableCount, depthLimited);
            }
            else if (type == typeof(string))
            {
                str += string.Format("{0}{1}={2}\n",tableEmptyStr, name, data);
            }
            else if (type.IsClass)
            {
                str += StructToString(data, name, tableCount,depthLimited,ignoreClassName);
            }
            else
            {
                str += string.Format("{0}{1}={2}\n",tableEmptyStr, name, data);
            }
            return str;
        }

        public static string ListToString(object data, string arrayName, int tableCount = 0, int depthLimited = 5)
        {
            var str = string.Empty;
            var tableEmptyStr = string.Empty;
            for (int i = 0; i < tableCount; i++)
            {
                tableEmptyStr += tableFormat;
            }
            str += tableEmptyStr;
            str += arrayName + " =\n";
            str += tableEmptyStr;
            str += "{\n";
            tableCount = tableCount + 1;
            IList objList = (IList) data;
            depthLimited = depthLimited + 1;
            for (int i = 0; i < objList.Count; i++)
            {
                str += ObjectToString(objList[i],"["+i+"]",tableCount,depthLimited);
            }
            str += tableEmptyStr;
            str += "}\n";
            return str;
        }

        public static string DictionaryToString(object data, string mapName, int tableCount = 0, int depthLimited = 5)
        {
            var str = string.Empty;
            var dic = (Dictionary<object, object>) data;
            var tableEmptyStr = string.Empty;
            for (int i = 0; i < tableCount; i++)
            {
                tableEmptyStr += tableFormat;
            }
            str += tableEmptyStr;
            str += "{\n";
            tableCount = tableCount + 1;
            foreach (var item in dic)
            {
                str += ObjectToString(item.Value,"["+item.Key+"]",tableCount,depthLimited);
            }
            str += tableEmptyStr;
            str += "}\n";
            return str;
        }
        
        public static string StructToString(object data,string structName,int tableCount = 0,int depthLimited = 5,HashSet<string> ignoreClassName = null)
        {
            if (ignoreClassName != null && ignoreClassName.Contains(structName))
            {
                return "";
            }
            var str = string.Empty;
            var classType = data.GetType();
            var properties = classType.GetFields();
            var tableEmptyStr = string.Empty;
            for (int i = 0; i < tableCount; i++)
            {
                tableEmptyStr += tableFormat;
            }

            str += tableEmptyStr;
            str += structName + " =";
            str += "\n";
            str += tableEmptyStr + "{";
            str += "\n";
            tableCount = tableCount + 1;

            for (int i = 0; i < properties.Length; i++)
            {
                var name = properties[i].Name;
                var type = properties[i].FieldType;
                var value = properties[i].GetValue(data);
                str += ObjectToString(value, name, tableCount,depthLimited,ignoreClassName);
            }
            str += tableEmptyStr + "}";
            str += "\n";
            return str;
        }
        
        
        public static Vector3 GetGroundPositionByScreenPosition(Camera camera,Vector2 pos)
        {
            Ray ray = camera.ScreenPointToRay(new Vector3(pos.x, pos.y, 0f));
            var origin = ray.origin;
            var direction = ray.direction;
            var p = (origin.y) / direction.y;
            return origin - direction * p;
        }

        public static float Distance(Coordinate a, Coordinate b)
        {
            var x = a.xAxis - b.xAxis;
            var z = a.zAxis - b.zAxis;
            return Mathf.Sqrt(x * x + z * z);
        }

        public static float3 Lerp(float3 a, float3 b, float percent)
        {
            float x = Mathf.Lerp(a.x, b.x, percent);
            float y = Mathf.Lerp(a.y, b.y, percent);
            float z = Mathf.Lerp(a.z, b.z, percent);
            return new float3(x,y,z);
        }

        //大号行军线
        public static bool IsWonderNpcBig(RtsNpcType type)
        {
            return type == RtsNpcType.NPC_MIRACLE_REINFORCE || type == RtsNpcType.NPC_MIRACLE_MOBILIZATION;
        }
    }
}
